package 简单的个人记账本;

import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
import java.util.regex.Pattern;

public class PersonalAccountBook {
    private int operationNumber;
    private static final ObjectMapper mapper = new ObjectMapper();
    private final String RECORD_FILE_PATH = "src/main/java/简单的个人记账本/record.jsonl";

    public void showMenu() {
        System.out.print("""
                ===== 个人记账本 =====
                1. 添加记录
                2. 查看所有记录
                3. 统计收支情况
                4. 按日期范围查询
                5. 退出
                """
        );
    }

    private void printInputOperationNumberReminder() {
        System.out.print("请输入操作编号：");
    }

    private void enterOperationNumber(Scanner scanner) {
        final int MAX_OPERATION_NUMBER = 5;
        final String ERROR = "输入失败！请输入一个1到" + MAX_OPERATION_NUMBER + "之间的整数！";

        printInputOperationNumberReminder();
        while (true) {
            if (scanner.hasNextInt()) {
                int input = scanner.nextInt();
                if (input > 0 && input <= MAX_OPERATION_NUMBER) {
                    operationNumber = input;
                    scanner.nextLine();
                    break;
                } else {    // 防止输入不在操作编码内的数字
                    scanner.nextLine();
                    System.out.println(ERROR);
                    printInputOperationNumberReminder();
                }
            } else {
                String line = scanner.nextLine();
                if (!line.trim().isEmpty()) {   // 防止输入多空行加字符串
                    System.out.println(ERROR);
                    printInputOperationNumberReminder();
                }
            }
        }
        System.out.println();
    }

    private void printError() {
        System.out.println("输入错误！");
    }

    private String inputType(Scanner scanner) {
        final String REMINDER = "请输入类型（收入/支出）：";

        System.out.print(REMINDER);
        while (true) {
            String type = scanner.nextLine().trim();
            if ("收入".equals(type) || "支出".equals(type)) {
                return type;
            } else {
                printError();
                System.out.print(REMINDER);
            }
        }
    }

    private double inputMoney(Scanner scanner) {
        final String REMINDER = "请输入金额：";

        System.out.print(REMINDER);
        double money;
        while (true) {
            if (scanner.hasNextDouble()) {
                money = scanner.nextDouble();
                scanner.nextLine();
                break;
            } else {
                String line = scanner.nextLine();
                if (!line.trim().isEmpty()) {   // 防止输入多空行加字符串
                    printError();
                    System.out.print(REMINDER);
                }
            }
        }
        return money;
    }

    private String inputDescription(Scanner scanner) {
        System.out.print("请输入描述（一行）：");
        return scanner.nextLine().trim();
    }

    private boolean isValidDate(String input) {
        final Pattern DATE_PATTERN =
                Pattern.compile("^\\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])$");
        if (!DATE_PATTERN.matcher(input).matches()) {
            return false; // 格式不符合
        }

        try {
            // 尝试用 LocalDate 解析，判断日期是否真实存在
            LocalDate.parse(input);
            return true;
        } catch (DateTimeParseException e) {
            return false; // 比如 2023-02-29 会在这里抛异常
        }
    }

    private String inputDate(Scanner scanner) {
        final String REMINDER = "请输入日期（yyyy-MM-dd）：";

        System.out.print(REMINDER);
        while (true) {
            String date = scanner.nextLine().trim();
            if (isValidDate(date)) {
                return date;
            } else {
                printError();
                System.out.print(REMINDER);
            }
        }
    }

    private void addRecord(Scanner scanner) {
        Record record = new Record();
        record.setType(inputType(scanner));
        record.setMoney(inputMoney(scanner));
        record.setDescription(inputDescription(scanner));
        record.setDate(inputDate(scanner));
        if (writeRecordsToFile(record)) {
            System.out.println("记录添加成功！");
            System.out.println();
        } else {
            System.out.println("记录添加失败！");
            System.out.println();
        }
    }

    private boolean writeRecordsToFile(Record record) {
        try {
            String json = mapper.writeValueAsString(record);
            try (BufferedWriter writer = new BufferedWriter(new FileWriter(RECORD_FILE_PATH, StandardCharsets.UTF_8, true))) {
                writer.write(json);
                writer.newLine();
            }
            return true;
        } catch (IOException e) {
            return false;
        }
    }

    private void printRecord(Record record) {
        System.out.println("类型：" + record.getType());
        System.out.println("金额：" + record.getMoney());
        System.out.println("描述：" + record.getDescription());
        System.out.println("日期：" + record.getDate());
        System.out.println();
    }

    private List<Record> loadAllRecords() throws IOException {
        List<Record> records = new ArrayList<>();
        try (BufferedReader reader = new BufferedReader(new FileReader(RECORD_FILE_PATH))) {
            String line;
            while ((line = reader.readLine()) != null) {
                try {
                    records.add(mapper.readValue(line, Record.class));
                } catch (IOException e) {
                    System.err.println("跳过损坏记录: " + line);
                }
            }
        }
        return records;
    }

    private void viewAllRecords() throws IOException {
        List<Record> records = loadAllRecords();
        for (Record record : records) {
            printRecord(record);
        }
    }

    private double getSumOfIncome() throws IOException {
        double sum = 0;
        List<Record> records = loadAllRecords();
        for (Record record : records) {
            if (record.getType().equals("收入")) {
                sum += record.getMoney();
            }
        }
        return sum;
    }


    private double getSumOfExpenditure() throws IOException {
        double sum = 0;
        List<Record> records = loadAllRecords();
        for (Record record : records) {
            if (record.getType().equals("支出")) {
                sum += record.getMoney();
            }
        }

        return sum;
    }

    private double getBalance() throws IOException {
        return getSumOfIncome() - getSumOfExpenditure();
    }

    private LocalDate inputStartDate(Scanner scanner) {
        System.out.println("请输入查询的起始日期（yyyy-MM-dd）：");

        return LocalDate.parse(inputDate(scanner));
    }

    private LocalDate inputTerminationDate(Scanner scanner) {
        System.out.println("请输入查询的终止日期（yyyy-MM-dd）：");

        return LocalDate.parse(inputDate(scanner));
    }

    private void queryRecordsByDateRange(LocalDate start, LocalDate end) throws IOException {
        List<Record> records = loadAllRecords();
        for (Record record : records) {
            LocalDate date = LocalDate.parse(record.getDate());
            if (date.isAfter(start) && date.isBefore(end) || date.isEqual(start) || date.isEqual(end)) {
                printRecord(record);
            }
        }
    }

    public void operate(Scanner scanner) throws IOException {
        enterOperationNumber(scanner);
        while (true) {
            switch (operationNumber) {
                case 1 -> addRecord(scanner);
                case 2 -> viewAllRecords();
                case 3 -> {
                    System.out.println("总收入：" + getSumOfIncome());
                    System.out.println("总支出：" + getSumOfExpenditure());
                    System.out.println("当前余额：" + getBalance());
                    System.out.println();
                }
                case 4 -> queryRecordsByDateRange(inputStartDate(scanner), inputTerminationDate(scanner));
                case 5 -> {
                    System.out.println("退出！");
                    return;
                }
            }
            showMenu();
            enterOperationNumber(scanner);
        }
    }
}
